#include "serialdialog.h"
//#include <QtGui>
#include <QtWidgets>
#include <QByteArray>
#include "keyboard.h"

SerialDialog::SerialDialog(QWidget* parent) : QDialog(parent),m_showInHex(false), m_sendHex(false)
{
    setupUi(this); 
    comboBoxPort->setCurrentIndex(0);

    OpenSerialPort();
    connect(comboBoxPort, SIGNAL(currentIndexChanged(QString)), this, SLOT(GetSerialPort(QString)));
    connect(comboBoxPort, SIGNAL(editTextChanged(QString)), this, SLOT(GetSerialPort(QString)));
    
    connect(checkBoxReceiveHex, SIGNAL(stateChanged(int)), this, SLOT(SetDisplayMode(int)));
    connect(checkBoxSendHex, SIGNAL(stateChanged(int)), this, SLOT(SetSendMode(int)));
    connect(checkBoxAutoSend, SIGNAL(stateChanged(int)), this, SLOT(AutoSend(int)));
    
    connect(pushButtonClearReceive, SIGNAL(clicked()), textEditReceive, SLOT(clear()));
    connect(pushButtonClearSend, SIGNAL(clicked()), textEditSend, SLOT(clear()));
    connect(pushButtonSend,SIGNAL(clicked()),this,SLOT(SendData()));
    connect(&m_autoSendTimer,SIGNAL(timeout()),this, SLOT(SendData()));


    textEditSend->setText("Hello YC Serial Linux ");
    
    lineEditAutoSendPeriod->setText("1000");

   textEditSend->installEventFilter(this);

   lineEditAutoSendPeriod->installEventFilter(this);

   textEditSend->setAttribute(Qt::WA_InputMethodEnabled,false);

   lineEditAutoSendPeriod->setAttribute(Qt::WA_InputMethodEnabled,false);
}

void SerialDialog::changeEvent(QEvent *e)
{
    QDialog::changeEvent(e);
    switch (e->type()) {
    case QEvent::LanguageChange:
        retranslateUi(this);
        break;
    default:
        break;
    }
}
bool  SerialDialog::eventFilter(QObject *obj, QEvent *event)
{
    int i=0;
    QString str;

    if((obj==textEditSend)||(obj==lineEditAutoSendPeriod))
    {
        if(event->type()==QEvent::MouseButtonPress)
        {
            if(obj==textEditSend)
            {
                 KeyBoard mKeyBoard(InputNet,textEditSend->text());
                connect(&mKeyBoard,SIGNAL(setvalue(QString)),obj,SLOT(setText(QString)));
                 mKeyBoard.exec();
             }
            else if (obj==lineEditAutoSendPeriod)
            {
                KeyBoard mKeyBoard(InputNet,lineEditAutoSendPeriod->text());
                connect(&mKeyBoard,SIGNAL(setvalue(QString)),obj,SLOT(setText(QString)));
                mKeyBoard.exec();
            }

        }
    }

    return QDialog::eventFilter(obj,event);
}

bool SerialDialog::OpenSerialPort(bool open)
{
    if(open)
    {
	m_serialName = comboBoxPort->currentText();
    m_serialRD = ::open(m_serialName.toLatin1().data(), O_RDONLY | O_NOCTTY | O_NONBLOCK);
	if(m_serialRD < 0)
	{
	    qWarning( "SerialDialog::OpenSerialPort Error:%s",strerror(errno));
	    return false;
	}
	
    m_serialWR = ::open(m_serialName.toLatin1().data(), O_WRONLY | O_NOCTTY | O_NONBLOCK);
	if(m_serialWR < 0)
	{
	    qWarning( "SerialDialog::OpenSerialPort Error:%s",strerror(errno));
	    return false;
	}
    SetSerialAttr(115200,8,0,1,0);
	GetSerialAttr();
	
    m_readThread = new ReadThread(m_serialRD);
    connect(m_readThread,SIGNAL(ReceivedData(char *,int)),this,SLOT(DataAvailable(char *,int)));
	m_readThread->start();
	
	return true;
    }
    else
    {
     CloseSerialPort();
	if(::close(m_serialRD) < 0 || ::close(m_serialWR) < 0)
	    return false;
	else
	    return true;
    }
}

void SerialDialog::CloseSerialPort()
{
    if(m_readThread->isRunning())
    {
	m_readThread->terminate();
	m_readThread->wait();
    }
}

void SerialDialog::GetSerialAttr()
{
    QString tmp;
    int index;
    tcgetattr(m_serialRD, &m_origIOS);
    
    int i,baudRate;
    //get baud rate of the serial port
    baudRate = cfgetospeed(&m_origIOS);
    
    for ( i= 0;  i < sizeof(speedArr) / sizeof(int);  i++)
    {
	if(baudRate == speedArr[i])
	{
	    tmp = QString("%1").arg(nameArr[i]);
	    break;
	}
    }
    comboBoxBaudRate->setCurrentIndex(comboBoxBaudRate->findText(tmp));
    
    //get the databits of the serial port
    if(m_origIOS.c_cflag & CS8)
    {
	index = 2;
    }
    else if(m_origIOS.c_cflag & CS7)
    {
	index = 1;
    }
    else if(m_origIOS.c_cflag & CS6)
    {
	index = 0;
    }
    comboBoxDataBit->setCurrentIndex(index);
    
    //get the parity
    if(!(m_origIOS.c_cflag & PARENB) && !(m_origIOS.c_iflag & INPCK))
    {
	index = 0;
    }
    else if((m_origIOS.c_cflag & PARENB) && (m_origIOS.c_cflag & PARODD) && (m_origIOS.c_iflag & INPCK))
    {
	index = 1;
    }
    else if((m_origIOS.c_cflag & PARENB) && !(m_origIOS.c_cflag & PARODD) && (m_origIOS.c_iflag & INPCK))
    {
	index = 2;
    }
    comboBoxParity->setCurrentIndex(index);
    
    //get the stopbit
    if(!(m_origIOS.c_cflag & CSTOPB))
    {
	index = 0;
    }
    else	
    {
	index = 1;
    }
    comboBoxStopBit->setCurrentIndex(index);
    
    connect(comboBoxBaudRate,SIGNAL(currentIndexChanged(QString)),this,SLOT(SetSerialPortAttr(QString)));
    connect(comboBoxDataBit, SIGNAL(currentIndexChanged(QString)), this, SLOT(SetSerialPortAttr(QString)));
    connect(comboBoxParity, SIGNAL(currentIndexChanged(QString)), this, SLOT(SetSerialPortAttr(QString)));
    connect(comboBoxStopBit, SIGNAL(currentIndexChanged(QString)), this, SLOT(SetSerialPortAttr(QString)));
    connect(comboBoxFlowControl, SIGNAL(currentIndexChanged(QString)), this, SLOT(SetSerialPortAttr(QString)));    
}

void SerialDialog::SetSerialPortAttr(QString)
{
    int baudRate = atoi(comboBoxBaudRate->currentText().toLatin1().data());
    int dataBit = atoi(comboBoxDataBit->currentText().toLatin1().data());
    int parity = comboBoxParity->currentIndex();
    int stopBit = atoi(comboBoxStopBit->currentText().toLatin1().data());
    int flowcontrol = comboBoxFlowControl->currentIndex();
    
    #ifdef DEBUG
    qDebug("Set:%d,%d,%d,%d\n",baudRate,dataBit,parity,stopBit,comboBoxFlowControl);
    #endif
    
    SetSerialAttr(baudRate,dataBit,parity,stopBit,flowcontrol);
}

void SerialDialog::RestoreSerialPort()
{
    int status = tcsetattr(m_serialRD, TCSANOW, &m_origIOS);
    if  (status != 0)
    {
	#ifdef DEBUG
	qDebug("tcsetattr m_serial:%s\n",strerror(errno));
	#endif
	return;
    }
    tcflush(m_serialRD,TCIOFLUSH);
}

void SerialDialog::GetSerialPort(const QString& text)
{
    if(m_serialName != text)
    {
	//restore the port setting and close the old port
	
	//RestoreSerialPort();
	OpenSerialPort(false);
	
	m_serialName = text;
	OpenSerialPort(true);
    }
}

void SerialDialog::DataAvailable(char* packet,int len)
{
    QByteArray array(packet,len);
    if(m_showInHex)
	array = array.toHex();
    QString result(array);
  //textEditReceive->append(result);
    //printf("##len:%d\r\n",len);
    textEditReceive->setText(textEditReceive->toPlainText()+result);
}

bool SerialDialog::SetSerialAttr(int baudrate,int databits,int parity,int stopbits,int flowcontrol)
{
    //get serial attribute
    struct termios options;
    if  ( tcgetattr( m_serialRD,&options)  !=  0)
    {
	#ifdef DEBUG
	qDebug("Get serial attribute error");
	#endif
	return false;
    }
    //set baud rate
    int i;
    for ( i= 0;  i < sizeof(speedArr) / sizeof(int);  i++)
    {
	if  (baudrate == nameArr[i])
	{
	    //tcflush(m_serial, TCIOFLUSH);
	    cfsetispeed(&options, speedArr[i]);
	    cfsetospeed(&options, speedArr[i]);
	    break;
	}
    }
    
    //clear c_cflag
    options.c_cflag |= (CLOCAL | CREAD);
    options.c_cflag &= ~CSIZE;
    //set databits
    switch (databits)
    {
	case 7:
	    options.c_cflag |= CS7;
	    break;
	case 8:
	    options.c_cflag |= CS8;
	    break;
	default:
	    #ifdef DEBUG
	    qDebug("Unsupported data size,%d",databits);
	    #endif
	    return false;
    }
    
    //set parity
    switch (parity)
    {
	case 0: //none
	    options.c_cflag &= ~PARENB;   
	    options.c_iflag &= ~INPCK;    
	    break;
	case 1: //odd
	    options.c_cflag |= (PARODD | PARENB); 
	    options.c_iflag |= INPCK;             
	    break;
	case 2: //even
	    options.c_cflag |= PARENB;     
	    options.c_cflag &= ~PARODD;  
	    options.c_iflag |= INPCK;       
	    break;
	case 3: //
	    options.c_cflag &= ~PARENB;
	    options.c_cflag &= ~CSTOPB;
	    break;
	default:
	    #ifdef DEBUG
	    qDebug("Unsupported parity %d",parity);
	    #endif
	    return false;
    }
    
    //set stopbits
    switch (stopbits)
    {
	case 1:
	    options.c_cflag &= ~CSTOPB;
	    break;
	case 2:
	    options.c_cflag |= CSTOPB;
	    break;
	default:
	    #ifdef DEBUG
	    qDebug("Unsupported stop bits %d",stopbits);
	    #endif
	    return false;
    }
    
    switch(flowcontrol)
    {
	case 0:     //no flow control
	    options.c_cflag &= ~CRTSCTS;
	    options.c_iflag &= ~(IXON | IXOFF | IXANY);
	    break;
	    
	case 1:     //hardware flow contorl
	    options.c_cflag |= CRTSCTS;
	    options.c_iflag &= ~(IXON | IXOFF | IXANY);
	    break;
	    
	case 2:     //software flow contorl
	    options.c_iflag |= (IXON | IXOFF | IXANY);
	    options.c_cflag &= ~CRTSCTS;
	    break;
    }
    
    options.c_iflag &= ~(INLCR | IGNCR | ICRNL);
    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);     //select raw input
    options.c_oflag &= ~OPOST;      //choose raw output
    
    tcflush(m_serialRD,TCIOFLUSH);
    options.c_cc[VTIME] = 10; 
    options.c_cc[VMIN] = 0; 
    if (tcsetattr(m_serialRD,TCSANOW,&options) != 0)
    {
	#ifdef DEBUG
	qDebug("SetupSerial");
	#endif
	return false;
    }
    return true;
}
void SerialDialog::StartSendData( int m_serial, QByteArray m_dataArray)
{
    struct timeval timeout;
    int fd;
    int sentLen = 0;
    int totalLen = m_dataArray.size();

    while(sentLen < totalLen)
    {
        FD_ZERO(&m_writeSet);
        FD_SET(m_serial,&m_writeSet);
        timeout.tv_sec = 0;
        timeout.tv_usec = 500;

        fd = select(m_serial + 1, NULL,&m_writeSet, NULL, &timeout);
        if(fd > 0)
        {
            if(FD_ISSET(m_serial,&m_writeSet) == 1)
            {
                sentLen += write(m_serial,m_dataArray.data()+sentLen,totalLen-sentLen);
            }
        }
    }
}
void SerialDialog::SendData()
{
    if(m_sendHex)
    {
    m_dataArray = textEditSend->text().toLatin1();//textEditSend->toPlainText().toLatin1();
	m_dataArray = QByteArray::fromHex(m_dataArray);
    }
    else
    m_dataArray = textEditSend->text().toLatin1();//textEditSend->toPlainText().toLatin1();
    
    //m_writeThread = new WriteThread(m_serialWR,m_dataArray);
   // m_writeThread->start();
    StartSendData(m_serialWR,m_dataArray);
}

void SerialDialog::SetDisplayMode(int hex)
{
    if(checkBoxReceiveHex->checkState() == Qt::Checked)
	m_showInHex = true;
    else if(checkBoxReceiveHex->checkState() == Qt::Unchecked)
	m_showInHex = false;
}

void SerialDialog::SetSendMode(int hex)
{
    if(checkBoxSendHex->checkState() == Qt::Checked)
	m_sendHex = true;
    else if(checkBoxSendHex->checkState() == Qt::Unchecked)
	m_sendHex = false;
}

void SerialDialog::AutoSend(int)
{
    if(checkBoxAutoSend->checkState() == Qt::Checked)
    {
    m_autoSendTimer.start(atoi(lineEditAutoSendPeriod->text().toLatin1().data()));
    }
    else
    {
	if(m_autoSendTimer.isActive())
	{
	    m_autoSendTimer.stop();
	}
    }
}

void SerialDialog::on_close_clicked()
{
    this->reject();
}
